//////////////////////////////////////////////////////////////////////////

#ifndef _PLAYSCREEN_H_
#include "playscreen.h"
#endif

//////////////////////////////////////////////////////////////////////////

#ifndef _FIELD_H_
#include "field.h"
#endif

#ifndef _BATTLEMENU_H_
#include "battlemenu.h"
#endif

#ifndef _STATISTICS_H_
#include "statistics.h"
#endif

#ifndef SETTINGSMENU_H_
#include "settingsmenu.h"
#endif
       
#ifndef _IMAGESURFACE_H_
#include "imagesurface.h"
#endif

#ifndef _CLIENT_H_
#include "client.h"
#endif
       
#ifndef _TIMER_H_
#include "timer.h"
#endif

#ifndef _THEME_H_
#include "theme.h"
#endif

#ifndef _SERVERSTATE_H_
#include "serverstate.h"
#endif

#ifndef _SERVERSTATEPACKET_H_
#include "serverstatepacket.h"
#endif

#ifndef _PLAYERSINFO_H_
#include "playersinfo.h"
#endif

#ifndef _CLIENTINPUTPACKET_H_
#include "clientinputpacket.h"
#endif

#ifndef _CLIENTMAPPARSER_H_
#include "clientmapparser.h"
#endif

#ifndef _SCOPEGUARD_H_
#include "scopeguard.h"
#endif

#include <SDL_mixer.h>
//////////////////////////////////////////////////////////////////////////


GUI_PlayScreen::GUI_PlayScreen(GUI *_gui, Client* _client):
    m_Client(_client),
    m_ID(-1),
    m_GUI(_gui),
    m_NetworkState(NoMessagesRecieved)
{
    m_ActiveMenu = Battlefield;
    m_Layers.resize(LayerCount);
    m_Layers[Battlefield] = new GUI_Field();
    m_Layers[Menu] = new GUI_BattleMenu();
    m_Layers[Settings] = new GUI_SettingsMenu();
    m_Layers[Statistics] = new GUI_Statistics();
    m_MenuBack =  new GUI_ImageSurface(GUI_Images::MenuBack);

    m_NeedRepaint = true;

    AddEventToObserve(Observer::KEY_INPUT_CHANGE);
    AddEventToObserve(Observer::MOUSE_POSITION_CHANGE);
   }


//////////////////////////////////////////////////////////////////////////


void GUI_PlayScreen::CreateMap( const std::string& _mapName )
{
    m_NetworkState = MapRecieved;
    GUI_Theme::ReloadMapImages(_mapName);
    Resize();
    std::string FullName = ".\\rc\\Maps\\" + _mapName + "\\map";
    ClientMapParser mapParser( FullName.c_str() );
    mapParser.Parse();

    const Points& points = mapParser.GetWalls();
    ((GUI_Field *) m_Layers[Battlefield])->DrawBackground(points);
    Mix_PlayMusic(GUI_Theme::GetMusic(GUI_Sounds::Game), -1);

}

//////////////////////////////////////////////////////////////////////////


GUI_PlayScreen::~GUI_PlayScreen()
{
    Mix_HaltMusic();
    for (int i = 0; i < m_Layers.size(); ++i)
        delete m_Layers[i];
}


//////////////////////////////////////////////////////////////////////////


void GUI_PlayScreen::Render(SDL_Surface *_mainSurface)
{
    if (m_NetworkState == MapRecieved)
    {
        if(SDL_MUSTLOCK(_mainSurface)) 
        {
            if(SDL_LockSurface(_mainSurface) < 0) return;
        }

        SDL_Color back = GUI_Theme::Color(GUI_Colors::SplashBackground);
        
        SDL_FillRect(_mainSurface, &_mainSurface->clip_rect, SDL_MapRGB(_mainSurface->format, back.r, back.g, back.b));
        m_Layers[Battlefield]->Render(_mainSurface, true);

        if ((m_ActiveMenu != Battlefield))
        {
            m_MenuBack->Blt(_mainSurface, 0.5, 0.5);

            m_Layers[m_ActiveMenu]->Render(_mainSurface, true);
        }

        if(SDL_MUSTLOCK(_mainSurface)) SDL_UnlockSurface(_mainSurface);
        SDL_Flip(_mainSurface); 
    }
}


//////////////////////////////////////////////////////////////////////////


void GUI_PlayScreen::Resize()
{
    for (int i = 0; i < LayerCount; ++i)
        m_Layers[i]->Resize();
    m_MenuBack->Resize();
}


//////////////////////////////////////////////////////////////////////////


void GUI_PlayScreen::SetID( int _ID )
{
    m_NetworkState = IDRecieved;
    ((GUI_Field *) m_Layers[Battlefield]) -> SetID(_ID);
    m_ID = _ID;
}


//////////////////////////////////////////////////////////////////////////


void GUI_PlayScreen::AddStateToRenderQueue( const ServerStatePacket& _packet )
{
    ((GUI_Field *) m_Layers[Battlefield])->AddStateToRenderQueue(_packet);

}


//////////////////////////////////////////////////////////////////////////


void GUI_PlayScreen::UpdateServerStatistics( const PlayersInfo& _playersInfo )
{
    ((GUI_Statistics *) m_Layers[Statistics])->UpdateServerStatistics(_playersInfo);

}


//////////////////////////////////////////////////////////////////////////


void GUI_PlayScreen::ProcessInputResult(int _result)
{
    static int ScreenMode = 0;
    static bool FullscreenMode = false;
    char Buffer[256];
    SDL_Rect *Resolution;
    m_NeedRepaint = true;
    switch (m_ActiveMenu)
    {
    case Menu:
        switch(_result)
        {
        case -2:
            m_NeedRepaint = false;
            break;
        case -1:
            break;
        case GUI_BattleMenu::Resume:
            m_ActiveMenu = Battlefield;
            break;
        case GUI_BattleMenu::Settings:
            {
                m_ActiveMenu = Settings;
                ScreenMode = m_GUI->GetCurrentScreenMode();
                Resolution = m_GUI->GetResolution(ScreenMode);
                sprintf(Buffer, "%dx%d", Resolution->w, Resolution->h);
                const char * res = Buffer;
                m_Layers[Settings]->SetLabelText(GUI_SettingsMenu::ResolutionValue, res);

                FullscreenMode = m_GUI->GetCurrentFullscreenMode();
                m_Layers[Settings]->SetLabelText(GUI_SettingsMenu::FullscreenModeValue, (FullscreenMode) ? ("Fullscreen") : ("Windowed"));
            }
            break;
        case GUI_BattleMenu::MainMenu:
            m_GUI->SwitchToScreen(GUI::Menu);
            break;
        case GUI_BattleMenu::Exit:
            m_GUI->SwitchToScreen(GUI::Exit);
            break;
        default:
            break;
        }
        break;
    case Settings:
        switch(_result)
        {
        case -2:
            m_NeedRepaint = false;
            break;
        case -1:
            break;
        case GUI_SettingsMenu::NextScreenResolution:
            {
                ScreenMode--;
                Resolution = m_GUI->GetResolution(ScreenMode);

                sprintf(Buffer, "%dx%d", Resolution->w, Resolution->h);
 
                const char * res = Buffer;
                m_Layers[Settings]->SetLabelText(GUI_SettingsMenu::ResolutionValue, res);
            }
            break;
        case GUI_SettingsMenu::PreviousScreenResolution:
            {
                ScreenMode++;
                Resolution = m_GUI->GetResolution(ScreenMode);
                sprintf(Buffer, "%dx%d", Resolution->w, Resolution->h);
                const char * res = Buffer;
                m_Layers[Settings]->SetLabelText(GUI_SettingsMenu::ResolutionValue, res);
            }
            break;
        case GUI_SettingsMenu::PreviousFullscreenMode:
        case GUI_SettingsMenu::NextFullscreenMode:
            {
                FullscreenMode = !FullscreenMode;
                m_Layers[Settings]->SetLabelText(GUI_SettingsMenu::FullscreenModeValue, (FullscreenMode) ? ("Fullscreen") : ("Windowed"));
            }
            break;
        case GUI_SettingsMenu::Apply:
            m_GUI->Resize(ScreenMode, FullscreenMode);
            m_ActiveMenu = Menu;
            break;
        case GUI_SettingsMenu::Cancel:
            m_ActiveMenu = Menu;
            break;
        default:
            break;
        }
        break;
    case Statistics:
        switch(_result)
        {
        case -2:
            m_NeedRepaint = false;
            break;
        case -1:
            break;
        case GUI_Statistics::Resume:
            {
            m_ActiveMenu = Battlefield;
            }
            break;
        }
    }
}


////////////////////////////////////////////////////////////////////////////////


void GUI_PlayScreen::OnKeyChanged(Input* _input)
{
    if (m_ActiveMenu == Battlefield)
    {
        if (m_NetworkState == MapRecieved)
        {
            Input::Key key = _input->GetKey();
            switch (key.m_Code)
            {
            case Input::W:
                m_ClientInput.SetUpPressed(key.m_State == Input::PRESSED);
                break;
            case Input::S:
                m_ClientInput.SetDownPressed(key.m_State == Input::PRESSED);
                break;
            case Input::A:
                m_ClientInput.SetLeftPressed(key.m_State == Input::PRESSED);
                break;
            case Input::D:
                m_ClientInput.SetRightPressed(key.m_State == Input::PRESSED);
                break;
            case Input::MOUSE1:
                m_ClientInput.SetShootPressed(key.m_State == Input::MOUSE_PRESSED);
                break;
            case Input::TAB:
                m_ClientInput.SetTabPressed(key.m_State == Input::PRESSED);
                m_ActiveMenu = Statistics;
                break;
            case Input::ESCAPE:
                m_ActiveMenu = Menu;
                m_NeedRepaint = true;
                break;
            case Input::EXIT:
                m_GUI->SwitchToScreen(GUI::Exit);
                break;

            default:
    //            assert(0);
                break;

            }
        }
    }
    else
        ProcessInputResult(m_Layers[m_ActiveMenu]->OnKeyChanged(_input));
}


////////////////////////////////////////////////////////////////////////////////


void GUI_PlayScreen::OnMousePositionChanged(Input* _input)           
{
    if (m_ActiveMenu == Battlefield)
    {
        if (m_NetworkState == MapRecieved)
        {
        Input::MousePosition pos = _input->GetMousePosition();
        m_ClientInput.SetMousePosition( 
            Point( 
            static_cast<float>(pos.x) / GUI_Theme::GetResizeFactor() * 1280.0,
            static_cast<float>(pos.y) / GUI_Theme::GetResizeFactor() * 1280.0) );
        }
    }
    else
        ProcessInputResult(m_Layers[m_ActiveMenu]->OnMousePositionChanged(_input));
}


////////////////////////////////////////////////////////////////////////////////


void GUI_PlayScreen::PreProcessInput()                     
{
    // Save previous input
    if (!m_PrevInput)
        delete m_PrevInput;
    m_PrevInput = new ClientInput(m_ClientInput);
}


////////////////////////////////////////////////////////////////////////////////


void GUI_PlayScreen::PostProcessInput()                     
{
    // Input changed?
    if ( m_ClientInput.InputChanged(*m_PrevInput) )
    {
        ClientInputPacket packet(Timer::GetCurrTime(), m_ClientInput);
        m_Client->SendClientInput(packet);
    }
}
